#ifndef __IANGELS_OP_H__
#define __IANGELS_OP_H__

#include <string>
#include <map>

#include "status.h"
#include "config.h"
#include "ugraph/tensor.h"

namespace iangels {

class OpBase {
public:
    OpBase(const Config& config):config_(config) {}
    virtual ~OpBase() {}
    const std::string id() const { return id_; }
    virtual Ret operator()(const std::vector<LazyDynamicTensor>& inputs,
        std::vector<LazyDynamicTensor>& outputs, bool inplace=false);

private:
    

    std::string id_;
    Config config_;
}; // class OpBase

typedef OpBase* (*CreateOpFunc)(const Config&);

class OpFactory {
public:
    ~OpFactory() {}

    static OpFactory& Instance() {
        static OpFactory factory;
        return factory;
    }

    OpBase* Create(const std::string& op_name, const Config& config)
    {
        auto it = op_map_.find(op_name);
        if (it == op_map_.end()) {
            return nullptr;
        }
        return it->second(config);
    }

    void append(const std::string& op_name, CreateOpFunc func) {
        auto it = op_map_.find(op_name);
        if (it != op_map_.end()) {
            return;
        }
        op_map_[op_name] = func;
    }

protected:
    OpFactory() {}

private:
    ::std::map<const std::string, CreateOpFunc> op_map_{};
}; // class OpBase


class OpRegistry {
public:
    OpRegistry() {}
    ~OpRegistry() {}

    OpRegistry(const std::string& name, CreateOpFunc func) {
        auto& factory = OpFactory::Instance();
        factory.append(name, func);
    }
}; // class OpRegistry

#define REGISTER_OP(op_name) \
    OpBase* Create##op_name(const Config& config) { return new op_name(config); } \
    OpRegistry g_##op_name(#op_name, Create##op_name);


}

#endif // __IANGELS_OP_H__